package org.artifact.core.plugin.csv;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.artifact.core.db.BaseCsv;
import org.artifact.core.lang.IPlugin;
import org.yaml.snakeyaml.Yaml;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.Watcher;
import cn.hutool.core.text.csv.CsvData;
import cn.hutool.core.text.csv.CsvReadConfig;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.hutool.setting.Setting;

public class CsvPlugin<E> implements IPlugin {
	
	static final Log log = LogFactory.get(CsvPlugin.class);
	
	static final int CSV_HEAD_ROW_COUNT = 4;
	
	/** 下载路径 */
	private String downloadUrl;
	/** 校验文件名 */
	private String verifyFileName;
	/** CSV文件夹 */
	private String csvFolderPath;
	/** 提供器的包 */
	private String providerPackage;
	/** 监控 */
	private boolean monitor;
	
	private WatchMonitor watchMonitor;
	
	private CsvReader reader;
	
	private CsvHelper helper;
	
	
	public CsvPlugin() {
		super();
	}


	public CsvPlugin(String downloadUrl, String verifyFileName, String csvFolderPath,String providerPackage, boolean monitor,E extendProxy) {
		super();
		this.downloadUrl = downloadUrl;
		this.verifyFileName = verifyFileName;
		this.csvFolderPath = csvFolderPath;
		this.providerPackage = providerPackage;
		this.monitor = monitor;
		CsvReadConfig config = new CsvReadConfig();
    	config.setFieldSeparator(';');
    	this.reader = new CsvReader(config);
    	this.helper = new CsvHelper(extendProxy);
	}


	public CsvHelper getHelper() {
		return helper;
	}




	@Override
	public boolean start() {
		
		try {
			// 1.下载
			if (StrUtil.isNotBlank(downloadUrl)) {
				download();
			}
			
			
	    	// 2.加载
			List<File> files =FileUtil.loopFiles(csvFolderPath, new FileFilter() {
				@Override
				public boolean accept(File pathname) {
					return StrUtil.endWith(pathname.getName(), ".csv") || StrUtil.endWith(pathname.getName(), ".yml");
				}
			});
			
			for (File file : files) {
				if (StrUtil.endWith(file.getName(), ".csv")) {
					paddingTable(file);
				}else if (StrUtil.endWith(file.getName(), ".yml")) {
					paddingGlobal(file);
				}
			}
			
			// 3.监控
			if (monitor) {
				monitor();
			}
			return true;
		} catch (Exception e) {
			log.error(e);
		}
		return false;
	}

	/**
	 * 填充table
	 * @param file
	 */
	private void paddingTable(File file) {
		Class<BaseCsv> clazz = getClassByFileName(file.getName());
		CsvData csvData = reader.read(file);
		LinkedHashMap<Integer,BaseCsv> csvTable = new LinkedHashMap<>(csvData.getRowCount() - CSV_HEAD_ROW_COUNT);
		for (int i = CSV_HEAD_ROW_COUNT; i < csvData.getRowCount(); i++) {
			BaseCsv baseCsv = ReflectUtil.newInstance(clazz,csvData.getRow(i));
			csvTable.put(baseCsv.getId(),baseCsv);
		}
		helper.cacheTable.put(clazz, csvTable);
	}
	
	/**
	 * 填充Global
	 * @param file
	 * @throws FileNotFoundException 
	 */
	private void paddingGlobal(File file) throws FileNotFoundException {
		Yaml yaml = new Yaml();
        Map result= (Map) yaml.load(new FileInputStream(file));
		helper.global.putAll(result);
	}


	/**
	 * 根据文件名取得结构体名称
	 * @param fileName
	 * @return
	 */
	private Class<BaseCsv> getClassByFileName(String fileName) {
		fileName =  StrUtil.removeSuffix(fileName, ".csv");
		// 2.转驼峰
		fileName = StrUtil.toCamelCase(fileName);
		// 3. 首字母大写
		fileName = StrUtil.upperFirst(fileName);
		
		Class<BaseCsv> clazz = ClassUtil.loadClass(providerPackage + "." + fileName +"Csv");
		
		return clazz;
	}

	/**
	 * 监控
	 */
	private void monitor() {
		watchMonitor = WatchMonitor.create(csvFolderPath, WatchMonitor.ENTRY_MODIFY);
		
		watchMonitor.setWatcher(new Watcher(){

    	    @Override
    	    public void onModify(WatchEvent<?> event, Path currentPath) {
    	    	String fileName = event.context().toString();
    	    	Class<BaseCsv> clazz = getClassByFileName(fileName);
    	    	if (clazz != null) {
    	    		File file = FileUtil.newFile(csvFolderPath + File.separator + fileName);
    	    		paddingTable(file);
				}
    	    	log.info("修改：{}-> {}", currentPath, fileName);
    	    }

			@Override
			public void onCreate(WatchEvent<?> event, Path currentPath) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onDelete(WatchEvent<?> event, Path currentPath) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onOverflow(WatchEvent<?> event, Path currentPath) {
				// TODO Auto-generated method stub
				
			}
    	});

    	//设置监听目录的最大深入，目录层级大于制定层级的变更将不被监听，默认只监听当前层级目录
    	watchMonitor.setMaxDepth(3);
    	//启动监听
    	watchMonitor.start();
	}



	private void download() {
		// 下载校验文件
		long contentSize = HttpUtil.downloadFile(downloadUrl + verifyFileName, csvFolderPath);
		if (contentSize > 0) {
			Setting setting = new Setting(new File(csvFolderPath + verifyFileName),Charset.forName("UTF-8"),false);
			setting.forEach((k,v) -> {
				String fielName = k.toString();
				if(FileUtil.exist(csvFolderPath + fielName)){
					String content = FileUtil.readString(csvFolderPath + fielName, "UTF-8");	
					String md5 = SecureUtil.md5(content);
					// 文件没有变动
					if (v.equals(md5)) {
						return;
					}
				}
				HttpUtil.downloadFile(downloadUrl + fielName, csvFolderPath);
			});
		}
	}

	@Override
	public boolean stop() {
		if (monitor && watchMonitor!=null) {
			watchMonitor.stop();
		}
		return true;
	}

	
	
	/**
	 * 扩展方法拦截器
	 * @author apple
	 *
	 */
	class ExtendMethodInterceptor implements MethodInterceptor {
		@Override
		public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
			String extendCacheKey = method.getName();
			for (Object object : args) {
				extendCacheKey += ("," + object.toString());
			}

			// 先查找缓存，缓存没有在真实查找
			Object object = helper.extendCache.computeIfAbsent(extendCacheKey, k -> {
				try {
					// 真实查找
					return proxy.invokeSuper(obj, args);
				} catch (Throwable e) {
					e.printStackTrace();
				}
				return null;
			});

			return object;
		}
	}
	
	public class CsvHelper{
		/**
		 * 全局配置
		 */
		private Map global = new HashMap<>();

		/**
		 * CSV缓存
		 */
		private Map<Class<BaseCsv>, LinkedHashMap<Integer,BaseCsv>> cacheTable = new HashMap<>();

		/**
		 * 扩展缓存
		 */
		private Map<String, Object> extendCache = new HashMap<>();
		/**
		 * 代理
		 */
		private E proxy;



		public CsvHelper(E extendProxy) {
			super();
			if (extendProxy!=null) {
				this.proxy = createProxy(extendProxy);
			}
		}



		private E createProxy(E extendProxy) {
			Enhancer enhancer = new Enhancer();
	        //设置目标类的字节码文件
	        enhancer.setSuperclass(extendProxy.getClass());
	        //设置回调函数
	        enhancer.setCallback(new ExtendMethodInterceptor());
	        return (E) enhancer.create();
		}


		@SuppressWarnings({ "unchecked", "unlikely-arg-type" })
		public <T extends BaseCsv> T getCsv(Class<T> clazz,int id){
			LinkedHashMap<Integer, BaseCsv> tables = cacheTable.get(clazz);
			if (tables.containsKey(clazz)) {
				return null;
			}
			return (T) tables.get(id);
		}

		public Map getGlobal() {
			return global;
		}

		public E getProxy() {
			return proxy;
		}
	}
}
